// file:arch/task/task.c
// time:2021.8.11
// autor:jiangxinpeng
// copyright:(C) by jiangxinpeng,All right are reserved.

#include <arch/task.h>
#include <arch/eflags.h>
#include <arch/segment.h>
#include <os/memspace.h>
#include <arch/interrupt.h>
#include <os/schedule.h>
#include <os/process.h>
#include <os/debug.h>

void UserFrameInit(trap_frame_t *frame)
{
    frame->ds = frame->es = frame->fs = frame->gs = SEG_SEL_USER_DATA;
    frame->cs = SEG_SEL_USER_CODE;
    frame->ss = SEG_SEL_USER_STACK;

    frame->edi = frame->esi = frame->ebp = 0;
    frame->eax = frame->ebx = frame->ecx = frame->edx = 0;

    frame->eflags = EFLAGS_IF | EFLAGS_IODPL3;
}

void UserThreadFrameBuild(trap_frame_t *frame, void *arg, void *func, void *thread_entry, void *stack_top)
{
    frame->ebx = (uint32_t)arg;
    frame->ecx = (uint32_t)func;
    frame->eip = (uint32_t)thread_entry;
    frame->esp = (uint32_t)stack_top;
}

void KernelFrameInit(trap_frame_t *frame)
{
    frame->eax = frame->ebx = frame->ecx = frame->edx = 0;
    frame->ds = frame->es = frame->gs = frame->fs = SEG_SEL_KERNEL_DATA;
    frame->cs = SEG_SEL_KERNEL_CODE;
    frame->ss = SEG_SEL_KERNEL_STACK;
    frame->edi = frame->esi = frame->esp = frame->ebp = 0;

    frame->eflags = EFLAGS_IF | EFLAGS_IODPL0;
}

void KernelThreadEntry(task_func_t function, void *arg)
{
    function(arg);
    TaskExit(0);
}

// thread stack build
void TaskStackBuild(task_t *task, task_func_t fun, void *arg)
{
    thread_stack_t *thread_stack;

    // alloc space in task kernel stack
    task->kstack -= sizeof(trap_frame_t);
    task->kstack -= sizeof(thread_stack_t);
    thread_stack = (thread_stack_t *)task->kstack;

    // set thread stack
    thread_stack->eip = KernelThreadEntry;
    thread_stack->function = fun;
    thread_stack->arg = arg;
    thread_stack->ebp = thread_stack->ebx = thread_stack->esi = thread_stack->edi = 0;
}

int TaskStackBuildWhenForking(task_t *child)
{
    trap_frame_t *frame = TASK_GET_TRAP_FRAME(child);
    // set sub task return val to 0
    frame->eax = 0;

    thread_stack_t *thread_stack = (thread_stack_t *)((uint32_t *)frame - 5);
    thread_stack->eip = (void *)InterruptExit;
    thread_stack->ebp = thread_stack->ebx = thread_stack->esi = thread_stack->edi = 0;

    // set stack top to stack top when do switch to
    child->kstack = (uint8_t *)&thread_stack->ebp;
    return 0;
}

int ProcessFrameInit(task_t *task, trap_frame_t *frame, char **argv, char **envp)
{
    vmm_t *vmm = task->vmm;

    // set stack space
    vmm->stack_end = USER_STACK_TOP;
    vmm->stack_start = vmm->stack_end - MEM_SPACE_STACK_SIZE_DEFAULT;

    if (MemSpaceMap(vmm, vmm->stack_start, 0, vmm->stack_end - vmm->stack_start, PROTE_USER | PROTE_WRITE, MEM_SPACE_MAP_FIXED | MEM_SPACE_MAP_STACK) < 0)
    {
        KPrint("[frame] vmm stack map failed!\n");
        return -1;
    }
    memset((uint8_t *)vmm->stack_start, 0, vmm->stack_end - vmm->stack_start);

    int argc = 0;
    char **new_envp = NULL;
    uint64_t arg_bottom = 0;
    // build envp to process stack
    argc = ProcessBuildArg(vmm->stack_end, (uint64_t *)&arg_bottom, (const char **)envp, (const char ***)&new_envp);

    char **new_argv = NULL;
    // build argv to process stack
    argc = ProcessBuildArg(arg_bottom, (uint64_t *)&arg_bottom, (const char **)argv, (const char ***)&new_argv);

    frame->ecx = argc;
    frame->ebx = (uint32_t)new_argv;
    frame->edx = (uint32_t)new_envp;

    if (!arg_bottom)
    {
        MemSpaceUnmap(vmm, vmm->stack_start, vmm->stack_end - vmm->stack_start);
        return -1;
    }

    frame->esp = arg_bottom;
    frame->ebp = frame->esp;
    return 0;
}

void ThreadStackDump(thread_stack_t *kstack)
{
    KPrint(PRINT_INFO "thread stack info: eip:%x func:%x arg:%x ebp:%x esi:%x edi:%x\n", kstack->eip, kstack->function, kstack->arg, kstack->ebp, kstack->esi, kstack->edi);
}